/*	--- 工厂方法 --- 


定义一个【用于创建对象的接口】，让【子类】决定【实例化哪一个类】。

【Factory Method】使【一个类的实例化】延迟到【其子类】。


===============================


工厂方法即Factory Method，是一种【对象创建型模式】。

工厂方法的目的是：
  使得【创建对象】和【使用对象】是【分离】的，并且【客户端Client】总是引用【抽象工厂】和【抽象产品】：

	┌─────────────┐      ┌─────────────┐
	│   Product   │      │   Factory   │
	└─────────────┘      └─────────────┘
	       ▲                    ▲
	       │                    │
	┌─────────────┐      ┌─────────────┐
	│ ProductImpl │<─ ─ ─│ FactoryImpl │
	└─────────────┘      └─────────────┘
	(IMP: Interface Message Processor, 接口信息处理器)


我们以具体的例子来说：假设我们希望实现一个解析【String 到 Number 的 Factory】 ，可以定义如下：*/
public interface Factory {
	Number parse(String s);
}


/*
有了工厂接口，再编写一个工厂的实现类： */
public class NumberFactoryImp1 implements NumberFactory {
	public Number parse(String s) {
		return new BigDecimal(s);
	}
}


/*
而【产品接口】是【Number】，【NumberFactoryImpl】返回的【实际产品是BigDecimal】。

Q: 那么【客户端】如何【创建NumberFactoryImpl】呢？
A: 通常我们会在【接口Factory】中【定义】一个【静态方法getFactory()】来返回【真正的子类】： */
public interface NumberFactory {
	// 创建方法: 
	Number parse(String s);

	// 获取工厂实例
	static NumberFactory getFactory() {
		return imp1;
	}

	static NumberFactory imp1 = new NumberFactoryImp1();
}



/*
在客户端中，我们只需要和【工厂接口NumberFactory】以及【抽象产品Number】打交道：*/
NumberFactory factory = NumberFactory.getFactory();
Number result = factory.parse("123.456");


/*
【调用方】可以完全忽略【真正的工厂NumberFactoryImpl】和【实际的产品BigDecimal】，这样做的【好处】是【允许创建产品的代码独立地变换，而不会影响到调用方】。

有的童鞋会问：一个简单的parse()需要写这么复杂的工厂吗？

实际上【大多数情况下】我们并【不需要抽象工厂】，而是【通过静态方法】直接返回【产品】，即：*/
public class NumberFactory {
	public static Number parse(String s) {
		return new BigDecimal(s);
	}
}


/*
这种【简化的使用静态方法】创建【产品】的【方式】称为【静态工厂方法（Static Factory Method）】。

【静态工厂方法】广泛地应用在【Java标准库】中。

例如：*/
Integer n = Integer.valueOf(100);


/*
【Integer】既是【产品】又是【静态工厂】。

它提供了【静态方法valueOf()】来【创建Integer】。

那么这种方式和【直接写new Integer(100)】有何区别呢？

我们观察valueOf()方法：*/
public final class Integer {
	public static Integer valueOf(int i) {
		if (i >= IntegerCache.low && i <= IntegerCache.high)
			return IntegerCache.cache[i + (-IntegerCache.low)];

		return new Integer(i);
	}
	// ....
}


/*
它的好处在于，【valueOf()内部】可能会使用【new】创建一个【新的Integer实例】，但也可能【直接返回】一个【缓存的Integer实例】。

对于【调用方】来说，没必要知道【Integer创建的细节】。


# PS: 工厂方法可以隐藏创建产品的细节，且不一定每次都会真正创建产品，完全可以返回缓存的产品，从而提升速度并减少内存消耗。

===========================================


如果【调用方】直接使用 Integer n = new Integer(100)，那么就失去了使用【缓存优化】的可能性。

我们经常使用的另一个【静态工厂方法】是【List.of()】：*/
List<String> list = List.of("A", "B", "C");


/*
这个【静态工厂方法】接收【可变参数】，然后返回【List接口】。

需要注意的是，【调用方】获取的【产品】总是【List接口】，而且并【不关心它的实际类型】。

即使【调用方】知道【List产品的实际类型】是【java.util.ImmutableCollections$ListN】，也不要去强制转型为子类，

因为【静态工厂方法List.of()】保证返回【List】，但也完全可以修改为返回【java.util.ArrayList】。

这就是【里氏替换原则】：返回【实现接口的任意子类】都可以满足【该方法的要求】，且【不影响】【调用方】。


# PS: 总是引用【接口】而非【实现类】，能允许【变换子类 而不影响调用方】，即尽可能【面向抽象编程】。


===================================


和List.of()类似，我们使用MessageDigest时，为了创建某个摘要算法，总是使用静态工厂方法getInstance(String)：*/
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");


/*
【调用方】通过【产品名称】获得【产品实例】，不但调用简单，而且【获得的引用】仍然是【MessageDigest这个抽象类】。



================================================


#	练习

使用静态工厂方法实现一个类似 20200202的整数 转换为 LocalDate：*/

public class LocalDateFactory {
    public static LocalDate fromInt(int yyyyMMdd) {
        ...
    }
}


/*==============================================


#	----- 工厂方法 の 小结 ----- 


1. 【工厂方法】是指: 定义【工厂接口】和【产品接口】，但如何创建【实际工厂】和【实际产品】被推迟到【子类实现】，从而使【调用方】只和【抽象工厂】与【抽象产品】打交道。

2. 【实际更常用】的是更简单的【静态工厂方法】，它允许【工厂内部】对【创建产品】进行【优化】。

3. 【调用方】尽量持有【接口】或【抽象类】，避免持有【具体类型的子类】，以便【工厂方法】能随时切换【不同的子类】返回，却不影响【调用方代码】。


=======================

模版： */
public interface Factory {
	Number parse(String s);
}

// --- 抽象工厂 --- 
public interface NumberFactory {
	// 创建方法: 
	Number parse(String s);

	// 获取工厂实例
	static NumberFactory getFactory() {
		return imp1;
	}

	static NumberFactory imp1 = new NumberFactoryImp1();
}

public class NumberFactoryImp1 implements NumberFactory {
	public Number parse(String s) {
		return new BigDecimal(s);
			// Java在java.math包中提供的API类BigDecimal，用来对超过16位有效位的数进行精确的运算。

			// 需要精确计算的结果，则必须使用BigDecimal类来操作。
	}
}


NumberFactory factory = NumberFactory.getFactory();
Number result = factory.parse("123.456");



// --- 静态工厂 --- 
public class NumberFactory {
	public static Number parse(String s) {
		return new BigDecimal(s);
	}
}


// --- 方法带内存优化 的 静态工厂 --- 
public final class Integer {
	public static Integer valueOf(int i) {
		if (i >= IntegerCache.low && i <= IntegerCache.high)
			return IntegerCache.cache[i + (-IntegerCache.low)];

		return new Integer(i);  

		/*
		默认IntegerCache.low 是-127，Integer.high是128，

		如果在这个区间内，他就会把【变量i】从 内存中的jdk的【常量池 IntegerCache.cache[0~255]】里 返回 常量i；

		但如果int i不在【常量池】范围内，就会去new一个Integer对象，
		*/
	}
	// ....
}


